package com.tau.egonetwork.threads;

import java.util.concurrent.Future;

import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

import com.tau.egonetwork.common.Log;
import com.tau.egonetwork.exceptions.EgoNetworkException;
import com.tau.egonetwork.gui.core.EgoNetworkProgressDialog;
import com.tau.egonetwork.gui.interfaces.IRequestGUI;
import com.tau.egonetwork.linkedin.ILinkedinResponseProgress;
import com.tau.egonetwork.linkedin.LinkedinAuthRequest;
import com.tau.egonetwork.linkedin.LinkedinAuthResponse;
import com.tau.egonetwork.linkedin.LinkedinExceptionHandler;
import com.tau.egonetwork.linkedin.LinkedinMainProfile;
import com.tau.egonetwork.linkedin.LinkedinProfileResponse;
import com.tau.egonetwork.linkedin.LinkedinRequest;
import com.tau.egonetwork.net.AccessToken;
import com.tau.egonetwork.net.EgoNetworkRequest;
import com.tau.egonetwork.threads.core.EgoNetworkCallable;
import com.tau.egonetwork.threads.core.ThreadPool;

public class LinkedinThread {
	
	private EgoNetworkProgressDialog progressDialog;
	private IRequestGUI linkedinGUIRequest;
	
	private String code;
	private Display display;
	
	private boolean progressDialogShown = true;
	
	private final static int PROGRESS_END_OBTAINING_AUTH = 5;
	private final static int PROGRESS_END_SENDING_REQUEST = 15;
	private final static int PROGRESS_START_PARSING_RESULT = 20;
	private final static int PROGRESS_START_FRIENDS = 25;
	private final static int PROGRESS_START_GROUPS = 45;
	private final static int PROGRESS_START_FRIENDS_MUTUAL = 55;
	private final static int PROGRESS_SAVING_FILE = 97;
	
	private final static String PROGRESS_MSG_AUTH = "Authenticating and obtaining access token...";
	private final static String PROGRESS_MSG_END_AUTH = "Sending Request for user data...";
	private final static String PROGRESS_MSG_PARSE_START = "Reading received data...";
	private final static String PROGRESS_MSG_READING_DATA = "Reading user data...";
	private final static String PROGRESS_MSG_READING_FRIENDS = "Reading friends data...";
	private final static String PROGRESS_MSG_REQUESTING_MUTUAL = "Requesting mutual friends data...";
	private final static String PROGRESS_MSG_READING_MUTUAL = "Reading mutual friends data...";
	private final static String PROGRESS_MSG_START_GROUP = "Reading groups data...";
	private final static String PROGRESS_MSG_SAVING_FILE = "Saving data to file...";
	
	private LinkedinThread(Shell parent, String code, IRequestGUI requestGUI) {
		this.linkedinGUIRequest = requestGUI;
		this.display = parent.getDisplay();
		this.code = code;

		progressDialog = new EgoNetworkProgressDialog(parent);
		progressDialog.setText("Linkedin Data Process");
		progressDialog.setTitle("Linkedin Profile Data Retreival In Progress");
		progressDialog.setSubtitle("Please wait while we attempt to get profile data..");
		progressDialog.setMessage(PROGRESS_MSG_AUTH);
	}

	public static void invoke(Shell parent, final String code, IRequestGUI progress) {

		final LinkedinThread thread = new LinkedinThread(parent, code, progress);
		thread.openDialog();
				
		ThreadPool.submitTask(new EgoNetworkCallable<Future<Void>, Void>() {
			
			@Override
			public Void call() throws Exception {
				thread.runThread();
				setArgument(null);
				return null;
			}
		});
	}

	private void openDialog() {
		progressDialog.open();
	}
	
	private void runThread() throws Exception {

		//notify GUI that thread has started to run:
		linkedinGUIRequest.onRequestIssued();
		
		//retrieve access token:
		EgoNetworkRequest networkRequest = new EgoNetworkRequest();
		LinkedinExceptionHandler exceptionHandler = new LinkedinExceptionHandler();
		
		AccessToken lat = null;
		try {
			lat = (AccessToken)networkRequest.doRequest(
				LinkedinAuthRequest.generateAuthCodeRequest(code),
				new LinkedinAuthResponse(), exceptionHandler);
		} catch (Exception e) {
			
			display.syncExec(new Runnable() {
				
				@Override
				public void run() {
					progressDialogShown = progressDialog.getVisible();
					progressDialog.close();
				}
			});
			
			linkedinGUIRequest.onResponseFailed(progressDialogShown,
					"Authentication Request: " + e.getMessage());
			return;
		}
		
		display.asyncExec(new Runnable() {
			
			@Override
			public void run() {
				progressDialog.setProgress(PROGRESS_END_OBTAINING_AUTH);
				progressDialog.setMessage(PROGRESS_MSG_END_AUTH);
			}
		});
				
		LinkedinProfileResponse response = new LinkedinProfileResponse();
		response.setAccessToken(lat.getAccessToken());
		response.setIProfileProgress(new ILinkedinResponseProgress() {
			
			private int friendsSize;
			private int numOfReuqests;
			private int currentRequest = 0;
			
			@Override
			public void onStartReadingFriends(int size) {
				friendsSize = size;
				display.asyncExec(new Runnable() {
					
					@Override
					public void run() {
						progressDialog.setMessage(PROGRESS_MSG_READING_FRIENDS);
						progressDialog.setProgress(PROGRESS_START_FRIENDS);
					}
				});
			}
			
			@Override
			public void onSaveToFile() {
				display.asyncExec(new Runnable() {
					
					@Override
					public void run() {
						progressDialog.setProgress(PROGRESS_SAVING_FILE);
						progressDialog.setMessage(PROGRESS_MSG_SAVING_FILE);
					}
				});				
			}
			
			@Override
			public void onResponseReceived() {
				display.asyncExec(new Runnable() {
					
					@Override
					public void run() {
						progressDialog.setMessage(PROGRESS_MSG_PARSE_START);
						progressDialog.setProgress(PROGRESS_END_SENDING_REQUEST);
					}
				});
			}
			
			@Override
			public void onReadMutualFriends(final int num) {
				final int progressWidth = PROGRESS_SAVING_FILE - PROGRESS_START_FRIENDS_MUTUAL;
				final int subProgressWidth = (int)Math.ceil(currentRequest / numOfReuqests);
				display.asyncExec(new Runnable() {
					
					@Override
					public void run() {
						progressDialog.setProgress(PROGRESS_START_FRIENDS_MUTUAL
								+ (int)Math.ceil(progressWidth * subProgressWidth * (num / friendsSize)));
					}
				});
			}
			
			@Override
			public void onReadFriend(final int num) {
				final int progressWidth = PROGRESS_START_GROUPS - PROGRESS_START_FRIENDS;
				display.asyncExec(new Runnable() {
					
					@Override
					public void run() {
						progressDialog.setProgress(PROGRESS_START_FRIENDS
								+ (int)Math.ceil(progressWidth * (num / friendsSize)));
					}
				});
			}
			
			@Override
			public void onProfileExtracted(String name) {
				linkedinGUIRequest.onResponseReceived(name);
				display.asyncExec(new Runnable() {

					@Override
					public void run() {
						progressDialog.setProgress(PROGRESS_START_PARSING_RESULT);
						progressDialog.setMessage(PROGRESS_MSG_READING_DATA);
					}
					
				});
			}

			@Override
			public void onReadGroups(int size) {
				display.asyncExec(new Runnable() {
					
					@Override
					public void run() {
						progressDialog.setMessage(PROGRESS_MSG_START_GROUP);
						progressDialog.setProgress(PROGRESS_START_GROUPS);
					}
				});
			}

			@Override
			public void onStartFriendRequests(int num) {
				numOfReuqests = num;
				display.asyncExec(new Runnable() {
					
					@Override
					public void run() {
						progressDialog.setMessage(PROGRESS_MSG_READING_MUTUAL);
						progressDialog.setProgress(PROGRESS_START_FRIENDS_MUTUAL);
					}
				});
			}

			@Override
			public void onIssueFriendRequest(int num) {
				currentRequest = num;
				display.asyncExec(new Runnable() {
					
					@Override
					public void run() {
						progressDialog.setMessage(PROGRESS_MSG_REQUESTING_MUTUAL);
					}
				});
			}
		});
		
		LinkedinMainProfile lin = null;
		boolean exceptionThrown = false;
		String exceptionMessage = null;
		try {

			lin = (LinkedinMainProfile)networkRequest.doRequest(
					LinkedinRequest.egoProfileRequest(lat.getAccessToken()), response,
					exceptionHandler);
						
		} catch (Exception e) {
			if (!(e instanceof EgoNetworkException)) {
				Log.writeExceptionToLog(e);
			}
			exceptionThrown = true;
			exceptionMessage = e.getMessage();
		} finally {
			
			display.syncExec(new Runnable() {
				
				@Override
				public void run() {
					progressDialogShown = progressDialog.getVisible();
				}
			});
			
			if (exceptionThrown) {
				display.syncExec(new Runnable() {
					
					@Override
					public void run() {
						progressDialog.close();
					}
				});
				linkedinGUIRequest.onResponseFailed(progressDialogShown, exceptionMessage);
			} else {
				
				//set progress to 100% and close dialog:
				//(dialog only closes completely when progress is 100%):
				display.asyncExec(new Runnable() {
					
					@Override
					public void run() {
						progressDialog.setProgress(100);
						progressDialog.close();
					}
				});
				linkedinGUIRequest.onResponseFinishedSuccessfuly(progressDialogShown, lin);
			}
		}
	}

}
